﻿using System;
using System.Xml;
using Notoric.Model.Internal;
using Notoric.Model.Packaging;

namespace Notoric.Model.Elements
{
    /// <summary>
    /// Represents a gradual change in dynamics.
    /// </summary>
    public class DynamicsProgressElement : BlockMarkElement
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DynamicsProgressElement"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this element.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="packagePart"/> or <paramref name="owner"/> is <see langword="null"/>.
        /// </exception>
        public DynamicsProgressElement(CompositionPart packagePart, BeatElement owner)
            : base(packagePart, owner)
        {
            ProgressType = DynamicsProgressType.Crescendo;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DynamicsProgressElement"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="CompositionPart"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="BeatElement"/>
        /// </para>
        /// <para>
        /// The beat that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="element">
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The XML element that holds the data of this object.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        internal DynamicsProgressElement(CompositionPart packagePart, BeatElement owner, XmlElement element)
            : base(packagePart, owner, element)
        {
            expressionAttribute = element.Attributes[PackageVocabulary.ExpressionAttribute];
            progressTypeAttribute = element.Attributes[PackageVocabulary.ProgressTypeAttribute];
        }

        #endregion

        #region BlockMarkElement Members

        /// <inheritdoc/>
        protected override XmlElement CreateSymbolElement(XmlDocument document)
        {
            var element = document.CreateElement(
                PackageVocabulary.PackageNamespacePrefix,
                PackageVocabulary.DynamicsProgressElementName,
                PackageVocabulary.PackageNamespaceUri
                );

            expressionAttribute = document.CreateAttribute(PackageVocabulary.ExpressionAttribute);
            progressTypeAttribute = document.CreateAttribute(PackageVocabulary.ProgressTypeAttribute);

            element.Attributes.Append(expressionAttribute);
            element.Attributes.Append(progressTypeAttribute);

            return element;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a textual expression that precises the dynamics change.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// A textual expression that further specifies the type of the dynamics change.
        /// Examples are <i>poco a poco</i> (bit by bit), <i>dal niente</i> (from nothing; out of silence)
        /// or <i>morendo</i> (dying away).
        /// </para>
        /// </value>
        public string Expression
        {
            get { return expressionAttribute.Value; }
            set
            {
                expressionAttribute.Value = value;

                RaisePropertyChanged("Expression");
            }
        }

        /// <summary>
        /// Gets or sets the type of the dynamics progress.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="DynamicsProgressType"/>
        /// </para>
        /// <para>
        /// The type of the dynamics progress.
        /// </para>
        /// </value>
        public DynamicsProgressType ProgressType
        {
            get { return (DynamicsProgressType)Enum.Parse(typeof(DynamicsProgressType), progressTypeAttribute.Value); }
            set
            {
                progressTypeAttribute.Value = value.ToString();

                RaisePropertyChanged("ProgressType");
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="Expression"/> property.
        /// </summary>
        private XmlAttribute expressionAttribute;
        /// <summary>
        /// Holds the value of the <see cref="ProgressType"/> property.
        /// </summary>
        private XmlAttribute progressTypeAttribute;

        #endregion
    }

    /// <summary>
    /// Decribes the type of the dynamics change.
    /// </summary>
    public enum DynamicsProgressType
    {
        /// <summary>
        /// Crescendo (get gradually louder).
        /// </summary>
        Crescendo,
        /// <summary>
        /// Decrescendo (get gradually softer).
        /// </summary>
        Decrescendo,
        /// <summary>
        /// Similar to <see cref="Decrescendo"/>, but usually includes
        /// a small rallentando.
        /// </summary>
        Diminuendo
    }
}
